﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;
using System.Reflection;
using System.Diagnostics;

namespace ASPItalia.ModelVirtualCasting.WPF.ViewModels
{
    public class AsyncViewModel : ViewModelBase
    {

        private bool _isAsyncWorking;

        public event AsyncCompletedEventHandler OperationCompleted;

        /// <summary>
        /// Get if an asynchronous operation is working
        /// </summary>
        public bool IsAsyncWorking
        {
            get { return _isAsyncWorking; }
            protected set
            {
                _isAsyncWorking = value;
                OnPropertyChanged("IsAsyncWorking");
            }
        }


        [DebuggerStepThrough()]
        protected virtual void ExecuteOperationAsync(Action action)
        {
            ExecuteOperationAsync(action, null);
        }

        [DebuggerStepThrough()]
        protected virtual void ExecuteOperationAsync(Action<AsyncOperation> action)
        {
            ExecuteOperationAsync(action, null);
        }

        [DebuggerStepThrough()]
        protected virtual void ExecuteOperationAsync(Action action, Action<AsyncCompletedEventArgs> uiCallback)
        {
            ExecuteOperationAsync(action, uiCallback, null);
        }

        [DebuggerStepThrough()]
        protected virtual void ExecuteOperationAsync(Action<AsyncOperation> action, Action<AsyncCompletedEventArgs> uiCallback)
        {
            ExecuteOperationAsync(action, uiCallback, null);
        }

        [DebuggerStepThrough()]
        protected virtual void ExecuteOperationAsync(Delegate @delegate, Action<AsyncCompletedEventArgs> uiCallback, params object[] args)
        {
            this.IsAsyncWorking = true;

            try
            {
                AsyncOperation operation = AsyncOperationManager.CreateOperation(null);

                // Aggiungo come primo parametro l'operazione
                if (@delegate.Method.GetParameters().Length > 0)
                {
                    if (args != null)
                        args = new object[] { operation }.Concat(args).ToArray();
                    else
                        args = new object[] { operation };
                }

                ThreadPool.QueueUserWorkItem(s =>
                {
                    bool done = false;
                    try
                    {
                        try
                        {
                            @delegate.DynamicInvoke(args);

                            // Callback con nessun errore
                            if (uiCallback != null)
                                operation.Post(p => uiCallback(new AsyncCompletedEventArgs(null, false, null)), null);

                            // Evento standard delle operazioni asincrone
                            operation.Post(se => OnOperationCompleted(new AsyncCompletedEventArgs(null, false, null)), null);

                            done = true;
                        }
                        finally
                        {
                            this.IsAsyncWorking = false;

                            if (done)
                                operation.OperationCompleted();
                        }
                    }
                    catch (TargetInvocationException te)
                    {
                        // Callback con errore
                        if (uiCallback != null)
                            operation.Post(p => uiCallback(new AsyncCompletedEventArgs(te.InnerException, false, null)), null);
                        else
                            operation.Post(p => HandleError((Exception)p), te.InnerException);

                        // Evento standard delle operazioni asincrone
                        operation.Post(se => OnOperationCompleted(new AsyncCompletedEventArgs(te.InnerException, false, null)), null);
                        operation.OperationCompleted();
                    }
                });
            }
            catch (Exception ex)
            {
                this.IsAsyncWorking = false;

                HandleError(ex);
            }
        }

        protected virtual void HandleError(Exception exception)
        {
            ExceptionLogWriter.Write("HandleError", exception);
            // TODO:
            //MessageBox.Show("Errore durante un operazione: " + exception.Message, Application.Current.MainWindow.Title, MessageBoxButton.OK, MessageBoxImage.Error);
        }

        protected virtual void OnOperationCompleted(AsyncCompletedEventArgs e)
        {
            if (OperationCompleted != null)
                OperationCompleted(this, e);
        }
    }
}

